//
//    rsq - Light, light, lightweight queuing system for sequential and parallel jobs.
//    Copyright (C) 2022  Marcus Pedersén marcus@marcux.org
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
//


use std::path::Path;
use std::fs::{ File, OpenOptions };
use std::io::{ prelude::*, BufReader, BufWriter };
use std::os::unix::fs::PermissionsExt;
use clap::{
    arg,
    Command,
    command,
    crate_version,
    crate_description,
    ArgMatches
};

use serde::Deserialize;
use reqwest::blocking::Client;
use log::{ Level, info, warn, error };
use file_owner::PathExt;
use rsqlib::client::*;
use rsqlib::client::RsqCommand::*;
use rsqlib::all::*;

/// Config file and default config
const CONFIG_FILE: &str = "/etc/rsqueue/rsq.conf";
const RSQD_PORT: u16 = 9921;
const VALIDATE_FILE: &str = "/etc/rsqueue/network.rsq";
const LOG_FILE: &str = "/var/log/rsqueue/rsqueue-client.log";
const RSQ_GROUP: &str = "rsqueue";

/// Contains configuration
/// read from rsq.conf file.
#[derive(Debug,Deserialize)] 
struct Config {
    rsqd_host: String,
    rsqd_port: Option<u16>,
    validate_file: Option<String>,
    log_file: Option<String>,
    rsq_group: Option<String>
}

/// Creates the correct url depending
/// on type or command and sends json
/// data to RsQueue daemon.
/// Returns response from daemon.
/// If Ok is returned, communication
/// to daemon is correct but if Ok
/// contains Err then deserialize
/// to ResdResponse failed.
fn send(cmd: &RsqCommand, conf: &Config, validate: String) -> Result<Result<RsqdResponse, String>, reqwest::Error> {
    let url = format!("http://{}:{}/rsq/request/command", conf.rsqd_host, conf.rsqd_port.unwrap());

    let rsqr = RsqRequest { cmd: cmd.clone(), validate: validate };
    
    let client = Client::new();
    let c = client.post(url)
	.json(&rsqr);

    // result json
    let res = c
	.send()?
	.json::<RsqdResponse>();

    match res {
	Ok(r) => {
	    Ok(Ok(r))
	},
	Err(e) =>
	    Ok(Err(format!("Wrong answer from RsQueue daemon, {}", e))),
    }
}



/// Function sets up validation
/// between RsQueue daemon and
/// this client.
fn validate_client(port: u16, conf: &Config) -> Result<(), String> {
    match validate_print_help() {
        Ok(_) => (),
        Err(e) => {
            return Err(e);
        },
    }
    
    match validate_host(port, conf.rsqd_host.clone(), None) {
        Ok(v) => {
            // TODO
            // Save validate string to file
            match File::create(conf.validate_file.clone().unwrap()) {
                Ok(f) => {
                    match f.metadata() {
                        Ok(m) => {
                            let mut perm = m.permissions();
                            perm.set_mode(0o640);
                            match f.set_permissions(perm) {
                                Ok(_) => (),
                                Err(e) => return Err(
                                    format!("Failed to set permissions on file: {}\n{}",
                                            conf.validate_file.clone().unwrap(), e)),

                            }
                        },
                        Err(e) => return Err(format!("Failed to read metadata from file: {}\n{}",
                                                     conf.validate_file.clone().unwrap(), e)),
                    }

                    match conf.validate_file.clone().unwrap().set_owner("root") {
                        Ok(_) => (),
                        Err(e) => return Err(format!("Failed to set owner of file: {}\n{}",
                                                     conf.validate_file.clone().unwrap(), e)),
                    }

                    match conf.validate_file.clone().unwrap().set_group(conf.rsq_group.clone().unwrap().as_str()) {
                        Ok(_) => (),
                        Err(e) => return Err(format!("Failed to set group of file: {}\n{}",
                                                     conf.validate_file.clone().unwrap(), e)),
                    }
                    
                    let mut bf = BufWriter::new(f);
                    match bf.write_all(v.as_bytes()) {
                        Ok(_) => (),
                        Err(e) => return Err(format!("Failed to write to file: {}\n{}",
                                             conf.validate_file.clone().unwrap(), e)),
                    }
                },
                Err(e) => return Err(format!("Failed to create file: {}\n{}",
                                     conf.validate_file.clone().unwrap(), e)),
            }
        },
        Err(e) => {
            return Err(e);
        }
    }

    Ok(())

}


/// Parses arg_string for both
/// long and short flags and
/// returns argument value.
/// If arg is a flag an empty
/// string is returned.
fn special_syntax_arg(long: &str, short: &str, all_args: &Vec<&str>, arg_str: &str) -> Result<Option<String>, String> {
    let mut arg_split: Vec<&str> = Vec::new();
    let mut argv = Ok(None);
    let mut found = false;
    
    if arg_str.contains(&long) {
	arg_split = arg_str.split(&long).collect();
	found = true;
    }
    else if arg_str.contains(&short) {
	arg_split = arg_str.split(&short).collect();
	found = true;
    }
    
    if arg_split.len() == 2 {
	if let Some(arg_part) = arg_split.get(1) {

	    let mut i = 100000;
	    for a in all_args {
		if let Some(f) =  arg_part.find(a) {
		    if f < i {
			i = f;
		    }
		}
	    }

	    if i == 100000 {
		argv = Ok(Some(arg_part
			       .trim()
			       .trim_matches('"')
			       .trim_matches('\'')
			       .to_string()
		));
	    }
	    else {
		let arg_div = arg_part.split_at(i);
		let arg_value = arg_div.0
		    .trim()
		    .trim_matches('"')
		    .trim_matches('\'');
		
		argv = Ok(Some(arg_value.to_string()));
	    }
	}
	else {
	    argv = Err("Failed to parse special syntax line.".to_string());
	}
    }
    else if found == true && arg_split.len() != 2 {
	argv = Err("Failed to parse special syntax line.\nEach argument can only be used once.".to_string());
    }
    
    argv
}


/// Parses sequence task file
/// Returns populated RsqCommand
/// with tasks and values from
/// special syntax line:
/// #--add-seq
/// A bool is also return, weather
/// line was found.
fn get_seq_tasks(filepath: String) -> Result<(RsqCommand, bool), String> {
    let mut special_found = false;
    let mut tasks: Vec<String> = vec!();
    let mut jobname = String::new();
    let mut jobid = false;
    let mut depend: Vec<u64> = vec!();
    let all_args = vec!("-d", "--depend", "-i", "--jobid", "-j", "--jobname");

    if ! Path::new(&filepath).exists() {
	return Err(format!("Taskfile: {} does not exist", filepath));
    }

    match File::open(&filepath) {
	Ok(f) => {
	    let reader = BufReader::new(f);

	    let mut line_no = 0;
	    
	    for line in reader.lines() {
		line_no += 1;

		match line {
		    Ok(l) => {
			let l_trim = l.trim();

			if l_trim.starts_with("#--add-seq ") {
			    special_found = true;
			    let args = l_trim.trim_start_matches("#--add-seq ").trim();

			    match special_syntax_arg("--jobname", "-j", &all_args, args) {
				Ok(job) => {
				    match job {
					Some(j) => {
					    if j.len() == 0 {
						return
						    Err(format!("On line: {} in file: {}\nArgument --jobname requires a value.",
								line_no,
								filepath));
					    }
					    else {
						jobname = j;
					    }
					},
					None => ()
				    }
				},
				Err(e) => {
				    return Err(format!("On line: {} in file: {}\n{}", line_no, filepath, e));
				}  
			    }

			    
			    match special_syntax_arg("--jobid", "-i", &all_args, args) {
				Ok(job) => {
				    match job {
					Some(j) => {
					    if j.len() == 0 {
						jobid = true;
					    }
					    else {
						return
						    Err(format!("On line: {} in file: {}\nArgument --jobid does not take a value.",
								line_no,
								filepath));
					    }
					},
					None => ()
				    }
				},
				Err(e) => {
				    return Err(format!("On line: {} in file: {}\n{}", line_no, filepath, e));
				}  
			    }

			    match special_syntax_arg("--depend", "-d", &all_args, args) {
				Ok(dep) => {
				    match dep {
					Some(d) => {
					    if d.len() == 0 {
						return
						    Err(format!("On line: {} in file: {}\nArgument --depend requires a value.",
								line_no,
								filepath));
					    }
					    else {
						let mut ids: Vec::<u64> = Vec::new();
						for i in d.split(',') {
						    if let Ok(id) = i.parse::<u64>() {
							ids.push(id);
						    }
						    else {
							return
						    Err(format!("On line: {} in file: {}\nArgument --depend requires a comma separated list of jobids.",
								line_no,
								filepath));
						    }
						}
						depend = ids;
					    }
					},
					None => ()
				    }
				},
				Err(e) => {
				    return Err(format!("On line: {} in file: {}\n{}", line_no, filepath, e));
				}  
			    }
			}
			else if l_trim.starts_with("#") {
			    continue;
			}
			else if l_trim.is_empty() {
			    continue;
			}
			else {
			    tasks.push(l_trim.to_string());
			}
		    }
		    Err(e) => {
			return Err(format!("Error reading line number: {} in file: {}\n{}",
				    line_no, filepath, e))
		    }
		}
	    }
	},
	Err(e) => {
	    return Err(format!("Failed to open file: {}\n{}", filepath, e));
	}
    }

    Ok(
	(
	    AddSeq {
		jobname: jobname,
		jobid: jobid,
		depend: depend,
		task: tasks
	    },
	    special_found
	)
    )
}



/// Parses parallell task file
/// Returns populated RsqCommand
/// with tasks and values from
/// special syntax line:
/// #--add-para
/// a bool is also returned weather
/// special syntax is found.
fn get_para_tasks(filepath: String) -> Result<(RsqCommand, bool), String> {
    let mut special_found = false;
    let mut tasks: Vec<String> = vec!();
    let mut jobname = String::new();
    let mut jobid = false;
    let mut depend: Vec<u64> = vec!();
    let mut cores: u64 = 0;
    let mut nodes: u64 = 0;
    let all_args = vec!("-d", "--depend", "-i", "--jobid", "-j", "--jobname", "-c", "--cores", "-n", "--nodes");

    if ! Path::new(&filepath).exists() {
	return Err(format!("Taskfile: {} does not exist", filepath));
    }

    match File::open(&filepath) {
	Ok(f) => {
	    let reader = BufReader::new(f);

	    let mut line_no = 0;
	    
	    for line in reader.lines() {
		line_no += 1;

		match line {
		    Ok(l) => {
			let l_trim = l.trim();

			if l_trim.starts_with("#--add-para ") {
			    special_found = true;
			    let args = l_trim.trim_start_matches("#--add-para ").trim();

			    match special_syntax_arg("--jobname", "-j", &all_args, args) {
				Ok(job) => {
				    match job {
					Some(j) => {
					    if j.len() == 0 {
						return
						    Err(format!("On line: {} in file: {}\nArgument --jobname requires a value.",
								line_no,
								filepath));
					    }
					    else {
						jobname = j;
					    }
					},
					None => ()
				    }
				},
				Err(e) => {
				    return Err(format!("On line: {} in file: {}\n{}", line_no, filepath, e));
				}  
			    }

			    
			    match special_syntax_arg("--jobid", "-i", &all_args, args) {
				Ok(job) => {
				    match job {
					Some(j) => {
					    if j.len() == 0 {
						jobid = true;
					    }
					    else {
						return
						    Err(format!("On line: {} in file: {}\nArgument --jobid does not take a value.",
								line_no,
								filepath));
					    }
					},
					None => ()
				    }
				},
				Err(e) => {
				    return Err(format!("On line: {} in file: {}\n{}", line_no, filepath, e));
				}  
			    }

			    match special_syntax_arg("--depend", "-d", &all_args, args) {
				Ok(dep) => {
				    match dep {
					Some(d) => {
					    if d.len() == 0 {
						return
						    Err(format!("On line: {} in file: {}\nArgument --depend requires a value.",
								line_no,
								filepath));
					    }
					    else {
						let mut ids: Vec::<u64> = Vec::new();
						for i in d.split(',') {
						    if let Ok(id) = i.parse::<u64>() {
							ids.push(id);
						    }
						    else {
							return
						    Err(format!("On line: {} in file: {}\nArgument --depend requires a comma separated list of jobids.",
								line_no,
								filepath));
						    }
						}
						depend = ids;
					    }
					},
					None => ()
				    }
				},
				Err(e) => {
				    return Err(format!("On line: {} in file: {}\n{}", line_no, filepath, e));
				}  
			    }

			    match special_syntax_arg("--cores", "-c", &all_args, args) {
				Ok(core) => {
				    match core {
					Some(c) => {
					    if c.len() == 0 {
						return
						    Err(format!("On line: {} in file: {}\nArgument --cores requires an integer as value.",
								line_no,
								filepath));
					    }
					    else {
						match c.parse::<u64>() {
						    Ok(n) => cores = n,
						    Err(_) => {
							return
							    Err(format!("On line: {} in file: {}\nArgument --cores requires an integer as value.",
								line_no,
								filepath));
						    }
						}
					    }
					},
					None => ()
				    }
				},
				Err(e) => {
				    return Err(format!("On line: {} in file: {}\n{}", line_no, filepath, e));
				}  
			    }

			    match special_syntax_arg("--nodes", "-n", &all_args, args) {
				Ok(node) => {
				    match node {
					Some(n) => {
					    if n.len() == 0 {
						return
						    Err(format!("On line: {} in file: {}\nArgument --nodes requires an integer as value.",
								line_no,
								filepath));
					    }
					    else {
						match n.parse::<u64>() {
						    Ok(no) => nodes = no,
						    Err(_) => {
							return
							    Err(format!("On line: {} in file: {}\nArgument --nodes requires an integer as value.",
								line_no,
								filepath));
						    }
						}
					    }
					},
					None => ()
				    }
				},
				Err(e) => {
				    return Err(format!("On line: {} in file: {}\n{}", line_no, filepath, e));
				}  
			    }
			}
			else if l_trim.starts_with("#") {
			    continue;
			}
			else if l_trim.is_empty() {
			    continue;
			}
			else {
			    tasks.push(l_trim.to_string());
			}
		    }
		    Err(e) => {
			return Err(format!("Error reading line number: {} in file: {}\n{}",
				    line_no, filepath, e))
		    }
		}
	    }
	},
	Err(e) => {
	    return Err(format!("Failed to open file: {}\n{}", filepath, e));
	}
    }

    Ok(
	(
	    AddPara {
		jobname: jobname,
		jobid: jobid,
		depend: depend,
		task: tasks,
		cores: cores,
		nodes: nodes
	    },
	    special_found
	)
    )
}


/// Command has been run:
/// rsq show queue or rsq
/// Parse args and prints
/// output to stdout.
/// Requires either Some args
/// or squeue as argument.
fn show_queue(args: Option<&ArgMatches>, squeue: Option<rsqlib::client::RsqCommand>, conf: &Config, validate: String) -> Result<(),()>{
    let queuec;
    let mut long = false;

    if let Some(a) = args {
	if a.contains_id("long") {
	    queuec = ShowQueue { long: true };
	    long = true;
	}
	else {
	    queuec = ShowQueue { long: false };
	}
    }
    else {
	if let Some(sq) = squeue {
	    queuec = sq;
	}
	else {
	    eprintln!("Internal error, failed to show queue");
	    write_log(&conf, Level::Error,
		      format!("Internal error, failed to show queue: Function show_queue called with wrong arguments"));

	    return Err(());
	}
    }

    match send(&queuec, &conf, validate) {
	Ok(res) => {
	    match res {
		Ok(r) => {
		    match r {
			RsqdResponse::ShowQueue {
			    queue
			} => {
			    // print queue
			    let mut line_long = String::new();
			    let mut line = String::new();

			    if long {
				let headline = "=".repeat(175);
				line_long = "-".repeat(175);
				println!("{headline}");
				println!("{:6} | {:20} | {:20} | {:^19} | {:^19} | {:^11} | {:^11} | {:^16} | {:10} | {:^10} | {}",
					 "Jobid", "User",
					 "Name", "Init",
					 "Start", "Cores alloc",
					 "Nodes alloc", "Tasks R/T/E:N",
					 "State", "Depend", "Nodes");
				println!("{headline}");
			    }
			    else {
				let headline = "=".repeat(121);
				line = "-".repeat(121);
				println!("{headline}");
				println!("{:6} | {:20} | {:20} | {:^11} | {:^11} | {:^16} | {:10} | {:^5}",
				    "Jobid", "User",
				    "Name", "Cores alloc",
				    "Nodes alloc", "Tasks R/T/E:N",
				    "State", "Nodes");
				println!("{headline}");
			    }

			    for j in queue {
				let mut name = "-".to_string();
				let mut start = "-".to_string();
				let mut depend = "-".to_string();
				let mut cores_alloc = "-".to_string();
				let mut nodes_alloc = "-".to_string();
				let mut nodes = "-".to_string();

				if j.name.is_some() {
				    name = j.name.unwrap();
				}

				if j.start.is_some() {
				    start = j.start.unwrap();
				}

				if j.depend.is_some() {
				    depend = j.depend.unwrap();
				}

				if j.nodes.is_some() {
				    nodes = j.nodes.unwrap();
				}

				if j.cores_alloc.is_some() {
				    cores_alloc = format!("{}", j.cores_alloc.unwrap());
				}

				if j.nodes_alloc.is_some() {
				    nodes_alloc = format!("{}", j.nodes_alloc.unwrap());
				}

				let tasks = format!("{}/{}/{}:{}",
						   j.no_tasks_exec, j.no_tasks_term,
						   j.no_tasks_err,  j.no_tasks);

				
				if long {
				    println!("{:6} | {:20} | {:20} | {:^19} | {:^19} | {:^11} | {:^11} | {:^16} | {:10} | {:^10} | {}",
 					     j.jobid, j.user,
					     name, j.init,
					     start, cores_alloc,
					     nodes_alloc, tasks,
					     j.state.to_string(), depend, nodes);
				    println!("{line_long}");
				}
				else {
				    println!("{:6} | {:20} | {:20} | {:^11} | {:^11} | {:^16} | {:10} | {:^5}",
					     j.jobid, j.user,
					     name, cores_alloc,
					     nodes_alloc, tasks,
					     j.state.to_string(), nodes);
				    println!("{line}");
				}
			    }

			    Ok(())
			},
			RsqdResponse::Err{ error} => {
			    eprintln!("Failed to get queue from RsQueue daemon.");
			    write_log(&conf, Level::Error,
				      format!("Failed to get queue from RsQueue daemon: {error}"));
			    Err(())
			},
			_ => {
			    eprintln!("Failed to parse response from RsQueue daemon.");
			    write_log(&conf, Level::Error,
				      format!("Failed to parse response from RsQueue daemon."));
			    Err(())
			}
		    }
		},
		Err(e) => {
		    eprintln!("Failed to parse response from RsQueue daemon.");
		    write_log(&conf, Level::Error,
			      format!("Failed to parse response from RsQueue daemon: {e}"));
		    Err(())
		},
	    }
	},
	Err(e) => {
	    eprintln!("Failed to connect to RsQueue daemon");
	    write_log(&conf, Level::Error,
		      format!("Failed to connect to RsQueue daemon: {e}"));
	    Err(())
	},
    }
}


/// Command has been run:
/// rsq show history
/// Parse args and prints
/// output to stdout.
fn show_history(args: &ArgMatches, conf: &Config, validate: String) -> Result<(),()>{
    let mut hist = ShowHistory { long: false, limit: 0 };
    let mut long = false;
    
    if args.contains_id("long") {
	hist = ShowHistory { long: true, limit: 0 };
	long = true;
    }
		    
    if args.contains_id("number") {
	if let Some(number) = args.get_one::<u64>("number") {
	    match hist {
		ShowHistory { long: lo , limit: _ } =>
		{
		    hist = ShowHistory { long: lo, limit: *number };
		},
		_ => (),
	    }
	}
	else {
	    eprintln!("Value is required for argument --number.\nTry -h or --help for help.");
	    return Err(());
	}
    }

    match send(&hist, &conf, validate) {
	Ok(res) => {
	    match res {
		Ok(r) => {
		    match r {
			RsqdResponse::ShowHistory {
			    history
			} => {
			    // print history
			    let mut line_long = String::new();
			    let mut line = String::new();

			    if long {
				let headline = "=".repeat(197);
				line_long = "-".repeat(197);
				println!("{headline}");
				println!("{:6} | {:20} | {:20} | {:^19} | {:^19} | {:^19} | {:^11} | {:^11} | {:^16} | {:10} | {:^10} | {}",
					 "Jobid", "User",
					 "Name", "Init",
					 "Start", "End",
					 "Cores alloc", "Nodes alloc",
					 "Tasks R/T/E:N", "State",
					 "Depend", "Nodes");
				println!("{headline}");
			    }
			    else {
				let headline = "=".repeat(121);
				line = "-".repeat(121);
				println!("{headline}");
				println!("{:6} | {:20} | {:20} | {:^11} | {:^11} | {:^16} | {:10} | {:^5}",
				    "Jobid", "User",
				    "Name", "Cores alloc",
				    "Nodes alloc", "Tasks R/T/E:N",
				    "State", "Nodes");
				println!("{headline}");
			    }

			    for j in history {
				let mut name = "-".to_string();
				let mut start = "-".to_string();
				let mut end = "-".to_string();
				let mut depend = "-".to_string();
				let mut cores_alloc = "-".to_string();
				let mut nodes_alloc = "-".to_string();
				let mut nodes = "-".to_string();

				if j.name.is_some() {
				    name = j.name.unwrap();
				}
				
				if j.start.is_some() {
				    start = j.start.unwrap();
				}

				if j.end.is_some() {
				    end = j.end.unwrap();
				}
				
				if j.depend.is_some() {
				    depend = j.depend.unwrap();
				}

				if j.nodes.is_some() {
				    nodes = j.nodes.unwrap();
				}

				if j.cores_alloc.is_some() {
				    cores_alloc = format!("{}", j.cores_alloc.unwrap());
				}

				if j.nodes_alloc.is_some() {
				    nodes_alloc = format!("{}", j.nodes_alloc.unwrap());
				}

				let tasks = format!("{}/{}/{}:{}",
						   j.no_tasks_exec, j.no_tasks_term,
						   j.no_tasks_err,  j.no_tasks);

				
				if long {
				    println!("{:6} | {:20} | {:20} | {:^19} | {:^19} | {:^19} | {:^11} | {:^11} | {:^16} | {:10} | {:^10} | {}",
 					     j.jobid, j.user,
					     name, j.init,
					     start, end,
					     cores_alloc, nodes_alloc,
					     tasks, j.state.to_string(),
					     depend, nodes);
				    println!("{line_long}");
				}
				else {
				    println!("{:6} | {:20} | {:20} | {:^11} | {:^11} | {:^16} | {:10} | {:^5}",
					     j.jobid, j.user,
					     name, cores_alloc,
					     nodes_alloc, tasks,
					     j.state.to_string(), nodes);
				    println!("{line}");
				}
			    }

			    Ok(())
			},
			RsqdResponse::Err{ error} => {
			    eprintln!("Failed to get history from RsQueue daemon.");
			    write_log(&conf, Level::Error,
				      format!("Failed to get history from RsQueue daemon: {error}"));
			    Err(())
			},
			_ => {
			    eprintln!("Failed to parse response from RsQueue daemon.");
			    write_log(&conf, Level::Error,
				      format!("Failed to parse response from RsQueue daemon."));
			    Err(())
			}
		    }
		},
		Err(e) => {
		    eprintln!("Failed to parse response from RsQueue daemon.");
		    write_log(&conf, Level::Error,
			      format!("Failed to parse response from RsQueue daemon: {e}"));
		    Err(())
		},
	    }
	},
	Err(e) => {
	    eprintln!("Failed to connect to RsQueue daemon.");
	    write_log(&conf, Level::Error,
		      format!("Failed to connect to RsQueue daemon: {e}"));
	    Err(())
	},
    }
}



/// Command has been run:
/// rsq show job
/// Parse args and prints
/// output to stdout.
fn show_job(args: &ArgMatches, conf: &Config, validate: String) -> Result<(),()>{
    if let Some(jobid) = args.get_one::<u64>("JOBID") {
	let jobc;
	if args.contains_id("details") {
	    jobc = ShowJob { details: true, id: *jobid };
	}
	else {
	    jobc = ShowJob { details: false, id: *jobid }
	}

	match send(&jobc, &conf, validate) {
	    Ok(job) => {
		match job {
		    Ok(j) => {
			match j {
			    RsqdResponse::ShowJob{ job } => {
				println!("{:?}", job);
				Ok(())
			    },
			    RsqdResponse::Err{ error} => {
				eprintln!("Failed to get job with id: {} from RsQueue daemon.", jobid);
	 			write_log(&conf, Level::Error,
					  format!("Failed to get job with id: {jobid} from RsQueue daemon: {error}"));
				Err(())
			    },
			    _ => {
		 		eprintln!("Failed to parse response from RsQueue daemon.");
				write_log(&conf, Level::Error,
					  format!("Failed to parse response from RsQueue daemon."));
				Err(())
			    }
			}
		    },
		    Err(e) => {
			eprintln!("Failed to parse response from RsQueue daemon.");
			write_log(&conf, Level::Error,
					  format!("Failed to parse response from RsQueue daemon: {e}"));
			Err(())
		    },
		}
		// print job

//print('Jobid:           {}'.format(response.get('jobid')))
//            print('Name:            {}'.format(response.get('name')))
//            print('User:            {}'.format(response.get('user')))
//            print('State:           {}'.format(response.get('state')))
//            if(len(response.get('depend')) > 0):
//                print('Depend:          {}'.format(response.get('depend')))
//            print('')
//            print('Initialize date: {}'.format(response.get('init_date')))
//            print('Start date:      {}'.format(response.get('start_date')))
//            print('End date:        {}'.format(response.get('end_date')))
//            print('')
//            print('Nodes: {}'.format(response.get('nodes')))
//            print('Number of allocated cores per node: {}'.format(response.get('cores_alloc')))
//            print('Number of tasks running:            {}'.format(response.get('no_tasks_running')))
//            print('Number of tasks terminated:         {}'.format(response.get('no_tasks_finished')))
//            print('Number of error tasks:              {}'.format(response.get('no_tasks_err')))
//            print('')
//            print('Total number of tasks:              {}'.format(len(response.get('tasks'))))
//            if(args.details):
//                print('\nOutput standard out:\n{}'.format(response.get('stdout')))
//                print('\nOutput standard error:\n{}'.format(response.get('stderr')))
//            print('')
//            print('')
//            print('')
//            print('Tasks:')
//
//            for t in response.get('tasks'):
//                print('Taskid:          {}'.format(t.get('taskid')))
//                print('Sequence number: {}'.format(t.get('seq')))
//                print('Command:         {}'.format(t.get('command')))
//                print('Node:            {}'.format(t.get('node')))
//                print('Exit code:       {}'.format(t.get('exit_code')))
//                print('Start date:      {}'.format(t.get('start_date')))
//                if(args.details):
//                    print('Run time:        {}'.format(t.get('runtime')))
//                    print('\nOutput standard out:\n{}'.format(t.get('stdout')))
//                    print('\nOutput standard error:\n{}'.format(t.get('stderr')))
//                print('')
//                print('')		
		
	    }
	    Err(e) => {
		eprintln!("Failed to connect to RsQueue daemon.");
		write_log(&conf, Level::Error,
			  format!("Failed to connect to RsQueue daemon: {e}"));
		Err(())
	    },
	}
    }
    else {
	eprintln!("JOBID must be a positive integer.");
	eprintln!("Try -h or --help for help.");
	Err(())
    }
}



/// Returns Config read from
/// rsq.conf, with default
/// values populated if values
/// are missing in file.
fn get_config() -> Result<Config, String> {
    match std::fs::read_to_string(CONFIG_FILE) {
	Ok(cont) => {
	    match toml::from_str::<Config>(&cont) {
		Ok(mut c) => {
		    if c.rsqd_port == None {
			c.rsqd_port = Some(RSQD_PORT);
		    }

		    if c.validate_file == None {
			c.validate_file = Some(VALIDATE_FILE.to_string());
		    }

		    if c.log_file == None {
			c.log_file = Some(LOG_FILE.to_string());
		    }

		    if c.rsq_group == None {
			c.rsq_group = Some(RSQ_GROUP.to_string());
		    }

		    if Path::new(&c.validate_file.as_ref().unwrap()).exists() {
			return Ok(c);
		    }
		    else {
			return Err(format!("File: {} does not exists\nCheck configuration", c.validate_file.unwrap()));
		    }
		}
		Err(e) => {
		    return Err(format!("Failed to parse config file: {}\n{}\nCheck configuration",
				CONFIG_FILE, e));
		}
	    }
	},
	Err(e) => {
	    return Err(format!("Failed to read config file: {}\n{}\nCheck configuration",
			CONFIG_FILE, e));
	}
    }
}

/// Sets up arguments and helptext
/// Returns matching arguments.
fn get_arg_matches() -> ArgMatches {
    command!()
	.subcommand(
	    Command::new("show")
		.subcommand_required(true)
		.about("Print information in RsQueue")
		.subcommand(
		    Command::new("queue")
			.about("Lists all running and queued jobs in RsQueue")
			.after_help("The values in Task column are the number of tasks: Running/Terminated/Error:Total")
			.arg(
			    arg!(-l --long "Prints a long and more detailed view of the queue")
			)
		)
		.subcommand(
		    Command::new("history")
			.about("Lists all terminated and canceled jobs in RsQueue")
			.args([
			    arg!(-l --long "Prints a long and more detailed view of the history"),
			    arg!(-n --number [NUMBER] "Limit the number of old jobs to be displayed. Value must be a positive integer.")
				.value_parser(clap::value_parser!(u64))
			])
		)
		.subcommand(
		    Command::new("job")
			.about("Shows information about specified job.")
			.args([
			    arg!(<JOBID> "Jobid for job to show")
				.value_parser(clap::value_parser!(u64)),
			    arg!(-d --details "Shows detailed information about specified job")
			])
		)
		.subcommand(
		    Command::new("status")
			.about("Shows information about all compute nodes available in RsQueue")
		)
	)
	.subcommand(
	    Command::new("add")
		.about("Add job to RsQueue")
		.args_conflicts_with_subcommands(true)
		.args([
		    arg!(-t --taskfile [TASKFILE] "Path to file containing all tasks that should be run in sequence or parallel. One task per line, empty lines and lines starting with # are ignored. TASKFILE must contain special syntax line for either parallel or squence job.
Special syntax in TASKFILE: \n#--add-para ...\n#--add-seq ..."),
		    arg!(-i --jobid "Jobid of this job will be returned on success as the only output. On failure the error message is printed.")
			.value_parser(clap::value_parser!(u64)),
		    arg!(-j --jobname [JOBNAME] "Name of specified job."),
		    arg!(-d --depend [DEPEND] "Comma separated list of other jobid that this job depends on. All jobs specified in this joblist have to terminate without errors before this job is started. If a depenedent job fails this job will also fail and will not be started.
Example: 1,5,9 (comma separated without whitespaces)")
	                .value_parser(clap::value_parser!(String)),
		])
                .subcommand(
		    Command::new("para")
			.about("Adds a parallel job of single threaded tasks to RsQueue")
			.args([
			    arg!(<TASKFILE> "Path to file containing all tasks that should be run in parallell. One task per line, empty lines and lines starting with # are ignored."),
			    arg!(-c --cores [CORES] "Number of cores to use on each node, --nodes is also required. Value must be a positive integer.")
				.value_parser(clap::value_parser!(u64))
				.requires("nodes"),
			    arg!(-n --nodes [NODES] "Number of nodes to use, RsQueue will choose which nodes to use. --cores is also required. Value must be a positive integer.")
				.value_parser(clap::value_parser!(u64))
				.requires("cores"),
			    arg!(-j --jobname [JOBNAME] "Name of specified job."),
			    arg!(-i --jobid "Jobid of this job will be returned on success as the only output. On failure the error message is printed.")
		 		.value_parser(clap::value_parser!(u64)),
			    arg!(-d --depend [DEPEND] "Comma separated list of other jobid that this job depends on. All jobs specified in this joblist have to terminate without errors before this job is started. If a depenedent job fails this job will also fail and will not be started.
Example: 1,5,9 (comma separated without whitespaces)")
	                        .value_parser(clap::value_parser!(String)),
			])
			.after_help(
			    "Number of tasks executed in parallel depends on number of cores and hosts specified.

If TASKFILE only contains one task on each line then both options --cores and --nodes are required.
If TASKFILE contains a line with the special syntax specifying required options then no options are required on the command line.
If TASKFILE contains the special syntax line and --core and --nodes are given as arguments these options will be used and the special syntax line will be ignored.

Special syntax in TASKFILE:
#--add-para --cores 5 --nodes 8"
			)
		)
		.subcommand(
		    Command::new("seq")
			.about("Adds a sequential job of single threaded tasks to RsQueue")
			.args([
			    arg!(<TASKFILE> "Path to file containing all tasks that should be run in sequence. One task per line, empty lines and lines starting with # are ignored."),
			    arg!(-j --jobname [JOBNAME] "Name of specified job."),
			    arg!(-i --jobid "Jobid of this job will be returned on success as the only output. On failure the error message is printed."),
			    arg!(-d --depend [DEPEND] "Comma separated list of other jobid that this job depends on. All jobs specified in this joblist have to terminate without errors before this job is started. If a depenedent job fails this job will also fail and will not be started.
Example: 1,5,9 (comma separated without whitespaces)")
			        .value_parser(clap::value_parser!(String)),
			])
			.after_help(
			    "All tasks will be executed one after another.

If TASKFILE contains a line with the special syntax specifying the name of the task this name will be used.
If TASKFILE contains the special syntax line and --jobname is given as arguments this options will be used and the special syntax line will be ignored.
If TASKFILE contains multiple special syntax lines the last line will be used.

Special syntax in TASKFILE:
#--add-seq --jobname The name of the job"
			)
		)
	)
	.subcommand(
	    Command::new("cancel")
		.about("Cancel jobs in RsQueue")
		.subcommand_required(true)
		.subcommand(
		    Command::new("job")
			.about("Cancel specified running or queued job.")
			.arg(
			    arg!(<JOBID> "Id number of job to cancel. Value must be a positive integer.")
				.value_parser(clap::value_parser!(u64))
			)
		)
	)
	.subcommand(
	    // User has to be root to run this subcommand
	    Command::new("conf")
		.about("Make config changes in RsQueue")
		.subcommand_required(true)
		.subcommand(
		    Command::new("node")
			.subcommand_required(true)
			.about("Configure compute nodes in RsQueue")
			//.subcommand(
			//    Command::new("add")
			//	.about("Add a new compute node to RsQueue.")
			//)
			//.subcommand(
			//    Command::new("remove")
			//	.about("Remove a compute node from RsQueue.")
			//)
			.subcommand(
			    Command::new("set")
				.about("Set status of a compute node in RsQueue.")
				.args([
				    arg!(<NODENAME> "Name of node to change state on in the RsQueue daemon."),
				    arg!(--online "Set node soft online and will display Soft Online until node is reacheble from RsQueue daemon and changes to Online, if node is not reachable RsQueue daemon will set node offline again.")
					.required_unless_present("offline")
					.conflicts_with("offline"),
				    arg!(--offline "Set node offline, if node still run processes, these will finish before set in Maintenance mode and Maintenance draining mode will show in status. While in Maitenance mode the node will not run any new jobs.")
					.required_unless_present("online")
					.conflicts_with("online")
				])
			)
		)
		.subcommand(
		    Command::new("validate")
			.about("Configure and validate client with daemon.")
			.arg(
			    arg!(-p --port <PORT> "Port that the validate server is listening on. Value must be a positive integer.")
				.value_parser(clap::value_parser!(u16))
			)
			.after_help("Without validation the clients on this host will not be able to communicate with daemon.
Start the validation server on the same server as the RsQueue daemon with:
rsqd --validate
and follow instructions."
			)
		)
	)
	.about(format!("{}
This is the client program
for RsQueue that interacts with the RsQueue daemon.
This tool is used to get status, add jobs and
configure the current RsQueue system.
If called without arguments the queue will be printed, same as: rsq show queue
", crate_description!()).as_str())
        .after_help(
"In RsQueue a job is a complete unit that is queued
and executed as one unit.
A task is one program that is executed inside a job that contains multiple tasks.
e
Additional help can be found for each sub-command with the flag -h or --help.
Example:
rsq add --help, will describe the add sub-command.
rsq add seq --help, will describe the seq sub-command of add sub-command
rsq add para --help, will describe the para sub-command of process add sub-command.

More information is available in man pages:
rsq(1), rsq.conf(5), rsqd(8), rsqd.conf(5), rsqd-node(8), rsqd-node.conf(5)")
	.author("")
	.long_version(
	    format!("v{}
Copyright (C) 2022 Marcus Pedersén
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Marcus Pedersén.", crate_version!()).as_str())
	.get_matches()
}


/// Writes message to log
/// with specified level.
/// Info, warn and error
/// is supported.
fn write_log(c: &Config, level: Level, msg: String) {

    let lf;
    match std::fs::OpenOptions::new()
	.append(true)
	.create(true)
	.open(c.log_file.clone().unwrap()) {
	    Ok(f) => lf = f,
	    Err(e) => {
		eprintln!("Failed to open log file: {}", c.log_file.clone().unwrap());
		eprintln!("{}", e);
		return ();	  
	    }
	}
	

    env_logger::Builder::new()
	.format(| buf, record | {
	    writeln!(
		buf,
		"{}  {:5} => {}",
		chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
		record.level(),
		record.args()
	    )
	})
	.target(env_logger::fmt::Target::Pipe(Box::new(lf)))
	.filter_level(log::LevelFilter::Info)
	.init();

    let user;
    match users::get_current_username() {
	Some(u) => {
	    match u.into_string() {
		Ok(ustr) => user = ustr,
		Err(_) => user = String::new(),
	    }
	},
	None => user = String::new(),
    }
    
    if level == Level::Info {
	info!("[User: {}] {}", user, msg);
    }
    else if level == Level::Warn {
	warn!("[User: {}] {}", user, msg);
    }
    if level == Level::Error {
	error!("[User: {}] {}", user, msg);
    }
}

/// Program start
/// Parse config file and
/// if valid parse args.
fn main() {
    let conf: Config;
    match get_config() {
	Ok(c) => conf = c,
	Err(e) => {
	    eprintln!("{}", e);
	    std::process::exit(1);
	}
    }

    
    let validate;
    match std::fs::read_to_string(conf.validate_file.as_ref().unwrap()) {
	Ok(v) => validate = v.trim().to_string(),
	Err(e) => {
	    eprintln!("Failed to read file: {}", conf.validate_file.as_ref().unwrap());
	    eprintln!("{e}");
	    eprintln!("Check configuration");
	    std::process::exit(2);
	}
    }
    
    let matches = get_arg_matches();

    
    match matches.subcommand() {
	Some(("show", show_m)) => {
	    match show_m.subcommand() {
		Some(("history", hist_m)) => {
		    match show_history(&hist_m, &conf, validate) {
			Ok(_) => (),
			Err(_) => std::process::exit(6),
		    }
		},
		Some(("job", job_m)) => {
		    if let Some(jobid) = job_m.get_one::<u64>("JOBID") {
			let jobc;
			if job_m.contains_id("details") {
			    jobc = ShowJob { details: true, id: *jobid };
			}
			else {
			    jobc = ShowJob { details: false, id: *jobid }
			}

			match send(&jobc, &conf, validate) {
			    Ok(_) => println!("json posted"),
			    Err(e) => {
				eprintln!("Failed to connect to RsQueue daemon.");
				write_log(&conf, Level::Error,
					  format!("Failed to connect to RsQueue daemon: {e}"));
				std::process::exit(4);
			    },
			}
		    }
		},
		Some(("queue", queue_m)) => {
		    match show_queue(Some(&queue_m), None, &conf, validate) {
			Ok(_) => (),
			Err(_) => std::process::exit(5),
		    } 
			
		},
		Some(("status", _ )) => {
		    match send(&ShowStatus, &conf, validate) {
			Ok(_) => println!("json posted"),
			Err(e) => {
			    eprintln!("Failed to connect to RsQueue daemon.");
			    write_log(&conf, Level::Error,
				      format!("Failed to connect to RsQueue daemon: {e}"));
			    std::process::exit(5);
			},
		    }
		},
		Some(_) => {
		    eprintln!("Failed to parse rsq show.\nTry -h or --help for help.");
		    std::process::exit(1);
		},
		None => {
		    eprintln!("Failed to parse rsq show.\nTry -h or --help for help.");
		    std::process::exit(1);
		}
	    }
	    
	},
	Some(("add", add_m)) => {
	    match add_m.subcommand() {
		Some(("seq", seq_m)) => {
		    let mut seq = RsqCommand::AddSeq {
			jobname: String::new(),
			jobid: false,
			depend: Vec::new(),
			task: Vec::new()
		    };
		    
		    if let Some(taskfile) = seq_m.get_one::<String>("TASKFILE") {
			match get_seq_tasks(taskfile.to_string()) {
			    
			    Ok((t, f)) => {
				if f {
				    seq = t;
				}
			    },
			    Err(e) => {
				eprintln!("{e}");
				std::process::exit(8);
			    }
			}
		    }

		    if seq_m.contains_id("jobname") {
			if let Some(jobname) = seq_m.get_one::<String>("jobname") {
			    match seq {
				RsqCommand::AddSeq {
				    jobname: _,
				    jobid: ji,
				    depend: d,
				    task: t } => {
				    seq = RsqCommand::AddSeq {
					jobname: jobname.to_string(),
					jobid: ji,
					depend: d,
					task: t
				    };
				},
				_ => (),
			    }
			}
			else {
			    eprintln!("Value is required for argument --jobname.\nTry -h or --help for help.");
			    std::process::exit(1);
			}
		    }

		    if seq_m.contains_id("depend") {
			if let Some(dep) = seq_m.get_one::<String>("depend") {
			    let mut ids: Vec::<u64> = Vec::new();
			    for i in dep.split(',') {
				if let Ok(id) = i.parse::<u64>() {
				    ids.push(id);
				}
				else {
				    eprintln!("Argument --depend requires a comma separated list of jobids.");
				    std::process::exit(11);
 				}
			    }

			    match seq {
				RsqCommand::AddSeq {
				    jobname: jn,
				    jobid: ji,
				    depend: _,
				    task: t } => {
				    seq = RsqCommand::AddSeq {
					jobname: jn,
					jobid: ji,
					depend: ids,
					task: t
				    };
				},
				_ => (),
			    }
			}
			else {
			    eprintln!("Value is required for argument --depend.\nTry -h or --help for help.");
			    std::process::exit(1);
			}
		    }

		    if seq_m.contains_id("jobid") {
			match seq {
			    RsqCommand::AddSeq {
				jobname: jn,
				jobid: _,
				depend: d,
				task: t } => {
				seq = RsqCommand::AddSeq {
				    jobname: jn,
				    jobid: true,
				    depend: d,
				    task: t
				};
			    },
			    _ => (),
			}
		    }

		    match seq {
			RsqCommand::AddSeq {
			    jobname: _,
			    jobid: _,
			    depend: _,
			    task: ref t
			} => println!("{} number of tasks found.", t.len()),
			_ => (),
		    }
		    
		    match send(&seq, &conf, validate) {
			Ok(_) => println!("json posted"),
			Err(e) => {
			    eprintln!("Failed to connect to RsQueue daemon.");
			    write_log(&conf, Level::Error,
				      format!("Failed to connect to RsQueue daemon: {e}"));
			    std::process::exit(3);
			},
		    }
		},
		Some(("para", para_m)) => {
		    let mut para = RsqCommand::AddPara {
			jobname: String::new(),
			jobid: false,
			depend: Vec::new(),
			task: Vec::new(),
			cores: 0,
			nodes: 0
		    };
		    
		    if let Some(taskfile) = para_m.get_one::<String>("TASKFILE") {
			match get_para_tasks(taskfile.to_string()) {
			    
			    Ok((t, f)) => {
				if f {
				    para = t;
				}
			    },
			    Err(e) => {
				eprintln!("{e}");
				std::process::exit(8);
			    }
			}
		    }

		    if para_m.contains_id("jobname") {
			if let Some(jobname) = para_m.get_one::<String>("jobname") {
			    match para {
				RsqCommand::AddPara {
				    jobname: _,
				    jobid: id,
				    depend: dep,
				    task: t,
				    cores: c,
				    nodes: n } => {
				    para = RsqCommand::AddPara {
					jobname: jobname.to_string(),
					jobid: id,
					depend: dep,
					task: t,
					cores: c,
					nodes: n
				    };
				}
				_ => ()
			    }
			}
			else {
			    eprintln!("Value is required for argument --jobname.\nTry -h or --help for help.");
			    std::process::exit(1);
			}
		    }

		    if para_m.contains_id("depend") {
			if let Some(dep) = para_m.get_one::<String>("depend") {
			    let mut depend: Vec<u64> = Vec::new();

			    for de in dep.split(',') {
				match de.trim().parse::<u64>() {
				    Ok(d) => depend.push(d),
				    Err(_) => {
					eprintln!("Argument --depend requires comma separated list of jobids as value.");
					eprintln!("Try -h or --help for help");
					std::process::exit(14);
				    }
				}
			    }
			    
			    match para {
				RsqCommand::AddPara {
				    jobname: j,
				    jobid: id,
				    depend: _,
				    task: t,
				    cores: c,
				    nodes: n } => {
				    para = RsqCommand::AddPara {
					jobname: j,
					jobid: id,
					depend: depend,
					task: t,
					cores: c,
					nodes: n
				    };
				}
				_ => ()
			    }
			}
			else {
			    eprintln!("Value is required for argument --depend.\nTry -h or --help for help.");
			    std::process::exit(1);
			}
		    }

		    if para_m.contains_id("jobid") {
			match para {
			    RsqCommand::AddPara {
				jobname: j,
				jobid: _,
				depend: d,
				task: t,
				cores: c,
				nodes: n } => {
				para = RsqCommand::AddPara {
				    jobname: j,
				    jobid: true,
				    depend: d,
				    task: t,
				    cores: c,
				    nodes: n
				};
			    }
			    _ => ()
			}
		    }

		    if para_m.contains_id("cores") {
			if let Some(cores) = para_m.get_one::<u64>("cores") {
			    match para {
				RsqCommand::AddPara {
				    jobname: j,
				    jobid: i,
				    depend: d,
				    task: t,
				    cores: _,
				    nodes: n } => {
				    para = RsqCommand::AddPara {
					jobname: j,
					jobid: i,
					depend: d,
					task: t,
					cores: *cores,
					nodes: n
				    };
				}
				_ => ()
			    }
			}
			else {
			    eprintln!("Value is required for argument --cores.\nTry -h or --help for help.");
			    std::process::exit(1);
			}
		    }

		    if para_m.contains_id("nodes") {
			if let Some(nodes) = para_m.get_one::<u64>("nodes") {
			    match para {
				RsqCommand::AddPara {
				    jobname: j,
				    jobid: i,
				    depend: d,
				    task: t,
				    cores: c,
				    nodes: _ } => {
				    para = RsqCommand::AddPara {
					jobname: j,
					jobid: i,
					depend: d,
					task: t,
					cores: c,
					nodes: *nodes
				    };
				}
				_ => ()
			    }
			}
			else {
			    eprintln!("Value is required for argument --nodes.\nTry -h or --help for help.");
			    std::process::exit(1);
			}
		    }

		    match para {
			RsqCommand::AddPara {
			    jobname: _,
			    jobid: _,
			    depend: _,
			    task: ref t,
			    cores: c,
			    nodes: n,
			} => {
			    if c <= 0 || n <= 0 {
				eprintln!("--cores and --nodes are required arguments");
				eprintln!("and must be positive integers.");
				eprintln!("Try -h or --help for help.");
				std::process::exit(22);
			    }
			    else {
				println!("{} number of tasks found.", t.len());
			    }
			}
			_ => (),
		    }
		    
		    match send(&para, &conf, validate) {
			Ok(_) => println!("json posted"),
			Err(e) => {
			    eprintln!("Failed to connect to RsQueue daemon.");
			    write_log(&conf, Level::Error,
				      format!("Failed to connect to RsQueue daemon: {e}"));
			    std::process::exit(3);
			},
		    }
		},
		None => {
		    if add_m.contains_id("taskfile") {
			let mut seq_found = false;
			let mut para = RsqCommand::AddPara {
			    jobname: String::new(),
			    jobid: false,
			    depend: Vec::new(),
			    task: Vec::new(),
			    cores: 0,
			    nodes: 0
			};

			let mut seq = RsqCommand::AddSeq {
			    jobname: String::new(),
			    jobid: false,
			    depend: Vec::new(),
			    task: Vec::new()
			};
			
			
			if let Some(taskfile) = add_m.get_one::<String>("taskfile") {
			    if let Ok((t, f)) =  get_seq_tasks(taskfile.to_string()) {
				if f {
				    seq_found = true;
				    seq = t;
				}
			    }

			    if ! seq_found {
				match  get_para_tasks(taskfile.to_string()) {
				    Ok((t, f)) => {
					if f {
					    para = t;
					}
					else {
					    eprintln!("Required special syntax line not found in file: {}", taskfile);
					    eprintln!("#--add-seq or #--add-para was expected.");
					    eprintln!("Try -h or --help for help.");
					    std::process::exit(6);
					}
				    },
				    Err(e) => {
					eprintln!("Failed to parse file: {}", taskfile);
					eprintln!("{}", e);
					std::process::exit(7);
				    }
				}
			    }
			}


			if add_m.contains_id("depend") {
			    if let Some(dep) = add_m.get_one::<String>("depend") {
				let mut ids: Vec::<u64> = Vec::new();
				for i in dep.split(',') {
				    if let Ok(id) = i.parse::<u64>() {
					ids.push(id);
				    }
				    else {
					eprintln!("Argument --depend requires a comma separated list of jobids.");
					std::process::exit(12);
 				    }
				}

				if seq_found {
				    match seq {
					RsqCommand::AddSeq {
					    jobname: j,
					    jobid: id,
					    depend: _,
					    task: t
					} =>
					    seq = RsqCommand::AddSeq {
						jobname: j,
						jobid: id,
						depend: ids,
						task: t
					    },
					_ => ()
				    }
				}
				else {
				    match para {
					RsqCommand::AddPara {
					    jobname: j,
					    jobid: id,
					    depend: _,
					    task: t,
					    cores: c,
					    nodes: n
					} =>
					    para = RsqCommand::AddPara {
						jobname: j,
						jobid: id,
						depend: ids,
						task: t,
						cores: c,
						nodes: n
						    
					    },
					_ => ()
				    }
				}
			    }
			    else {
				eprintln!("Value is required for argument --depend.\nTry -h or --help for help.");
				std::process::exit(1);
			    }
			}

			if add_m.contains_id("jobname") {
			    if let Some(job) = add_m.get_one::<String>("jobname") {
				if seq_found {
				    match seq {
					RsqCommand::AddSeq {
					    jobname: _,
					    jobid: id,
					    depend: d,
					    task: t
					} =>
					    seq = RsqCommand::AddSeq {
						jobname: job.to_string(),
						jobid: id,
						depend: d,
						task: t
					    },
					_ => ()
				    }
				}
				else {
				    match para {
					RsqCommand::AddPara {
					    jobname: _,
					    jobid: id,
					    depend: d,
					    task: t,
					    cores: c,
					    nodes: n
					} =>
					    para = RsqCommand::AddPara {
						jobname: job.to_string(),
						jobid: id,
						depend: d,
						task: t,
						cores: c,
						nodes: n
						    
					    },
					_ => ()
				    }
				}
			    }
			    else {
				eprintln!("Value is required for argument --jobname.\nTry -h or --help for help.");
				std::process::exit(1);
			    }
			}

			if add_m.contains_id("jobid") {
			    if seq_found {
				match seq {
				    RsqCommand::AddSeq {
					jobname: j,
					jobid: _,
					depend: d,
					task: t
				    } =>
					seq = RsqCommand::AddSeq {
					    jobname: j,
					    jobid: true,
					    depend: d,
					    task: t
					},
				    _ => ()
				}
			    }
			    else {
				match para {
				    RsqCommand::AddPara {
					jobname: j,
					jobid: _,
					depend: d,
					task: t,
					cores: c,
					nodes: n
				    } =>
					para = RsqCommand::AddPara {
					    jobname: j,
					    jobid: true,
					    depend: d,
					    task: t,
					    cores: c,
					    nodes: n
					},
				    _ => ()
				}
			    }
			}

			if seq_found {
			    match seq {
				RsqCommand::AddSeq {
				    jobname: _,
				    jobid: _,
				    depend: _,
				    task: ref t
				} => println!("{} number of tasks found.", t.len()),
				_ => (),
			    }
			    
			    match send(&seq, &conf, validate) {
				Ok(_) => println!("json posted"),
				Err(e) => {
				    eprintln!("Failed to connect to RsQueue daemon.");
				    write_log(&conf, Level::Error,
					      format!("Failed to connect to RsQueue daemon: {e}"));
				    std::process::exit(6);
				},
			    }
			}
			else {
			    match para {
				RsqCommand::AddPara {
				    jobname: _,
				    jobid: _,
				    depend: _,
				    task: ref t,
				    cores: c,
				    nodes: n,
				} => {
				    if c <= 0 || n <= 0 {
					eprintln!("--cores and --nodes are required arguments");
					eprintln!("and must be positive integers.");
					eprintln!("Try -h or --help for help.");
					std::process::exit(22);
				    }
				    else {
					println!("{} number of tasks found.", t.len());
				    }
				},
				_ => (),
			    }
			    
			    match send(&para, &conf, validate) {
				Ok(_) => println!("json posted"),
				Err(e) => {
				    eprintln!("Failed to connect to RsQueue daemon.");
				    write_log(&conf, Level::Error,
					      format!("Failed to connect to RsQueue daemon: {e}"));
				    std::process::exit(6);
				},
			    }
			}
		    }
		    else {
			eprintln!("error: The following required arguments were not provided:");
			eprintln!("    --taskfile <TASKFILE>");
			eprintln!("USAGE:");
			eprintln!("    rsq add --taskfile <TASKFILE>");
			eprintln!("    rsq add <SUBCOMMAND>");
                        eprintln!("");
			eprintln!("For more information try --help");
		    }
		},
		_ => eprintln!("Failed to parse arguments for rsq add"),
	    }
	}
	Some(("cancel", cancel_m)) => {
	    match cancel_m.subcommand() {
		Some(("job", job_m)) => {
		    if let Some(jobid) = job_m.get_one::<u64>("JOBID") {
			match send(&CancelJob { id: *jobid }, &conf, validate) {
			    Ok(_) => println!("json posted"),
			    Err(e) => {
				eprintln!("Failed to connect to RsQueue daemon.");
				write_log(&conf, Level::Error,
					  format!("Failed to connect to RsQueue daemon: {e}"));
				std::process::exit(6);
			    },
			}
		    }
		},
		Some((_, _)) => {
		    eprintln!("Failed to parse: rsq cancel");
		    write_log(&conf, Level::Error,
			      format!("Failed to parse: rsq cancel")); 
		    std::process::exit(1);
		},
		None => {
		    eprintln!("Failed to parse: rsq cancel");
		    write_log(&conf, Level::Error,
			      format!("Failed to parse: rsq cancel")); 
		    std::process::exit(1);
		}
	    }
	},
	Some(("conf", conf_m)) => {
	    match conf_m.subcommand() {
		Some(("node", node_m)) => {
		    match node_m.subcommand() {
			Some(("set", set_m)) => {
			    if let Some(nodename) = set_m.get_one::<String>("NODENAME") {
				let mut online = true;

				if set_m.contains_id("offline") {
				    online = false;
				}

				let nodec = ConfNodeSet { nodename: nodename.to_string(), online: online };

				match send(&nodec, &conf, validate) {
				    Ok(_) => println!("json posted"),
				    Err(e) => {
					eprintln!("Failed to connect to RsQueue daemon.");
					write_log(&conf, Level::Error,
						  format!("Failed to connect to RsQueue daemon: {e}")); 
					std::process::exit(5);
				    },
				}
			    }
			},
			Some(_) => {
			    eprintln!("Failed to parse rsq conf node\nTry -h or --help for help.");
			    std::process::exit(1);
			},
			None => {
			    eprintln!("Failed to parse rsq conf node\nTry -h or --help for help.");
			    std::process::exit(1);
			}
		    }
		},
		Some(("validate", val_m)) => {
		    if let Some(port) = val_m.get_one::<u16>("port") {
			
			match validate_client(*port, &conf) {
			    Ok(_) => println!("Validation SUCCESS!!"),
			    Err(e) => {
				eprintln!("{e}");
				write_log(&conf, Level::Error,
					  format!("{e}")); 
				std::process::exit(7);
			    },
			}
		    }
		},
		Some(_) => {
		    eprintln!("Failed to parse rsq conf\nTry -h or --help for help.");
		    std::process::exit(1);
		},
		None => {
		    eprintln!("Failed to parse rsq conf\nTry -h or --help for help.");
		    std::process::exit(1);
		},
	    }
	},
	None => {
	    // rsq called without arguments
	    match show_queue(None, Some(ShowQueue { long: false }), &conf, validate) {
	    	Ok(_) => (),
	    	Err(_) => std::process::exit(5),
	    }
	},
	_ => eprintln!("Failed to parse arguments for rsq"),
    }
}
